<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Netty入门-第二话 | 石银的小酒屋</title><meta name="keywords" content="Netty"><meta name="author" content="Alix"><meta name="copyright" content="Alix"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="对Netty的架构进行了解析，主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。">
<meta property="og:type" content="article">
<meta property="og:title" content="Netty入门-第二话">
<meta property="og:url" content="http://example.com/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/index.html">
<meta property="og:site_name" content="石银的小酒屋">
<meta property="og:description" content="对Netty的架构进行了解析，主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg">
<meta property="article:published_time" content="2022-04-15T06:21:58.000Z">
<meta property="article:modified_time" content="2023-04-20T10:49:24.331Z">
<meta property="article:author" content="Alix">
<meta property="article:tag" content="Netty">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg"><link rel="shortcut icon" href="/assetse/%E5%A4%B4%E5%83%8F.jpg"><link rel="canonical" href="http://example.com/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Netty入门-第二话',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-04-20 18:49:24'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/self/Kimbiedark.css"><meta name="generator" content="Hexo 6.2.0"><link rel="alternate" href="/atom.xml" title="石银的小酒屋" type="application/atom+xml">
</head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/assetse/%E5%A4%B4%E5%83%8F.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">23</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/playlist/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bangumis/"><i class="fa-fw fas fa-video"></i><span> 番剧</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">石银的小酒屋</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/playlist/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bangumis/"><i class="fa-fw fas fa-video"></i><span> 番剧</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Netty入门-第二话</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-04-15T06:21:58.000Z" title="发表于 2022-04-15 14:21:58">2022-04-15</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-04-20T10:49:24.331Z" title="更新于 2023-04-20 18:49:24">2023-04-20</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Netty/">Netty</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Netty/%E5%85%A5%E9%97%A8/">入门</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">12.5k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>49分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="Netty入门-第二话"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="Netty-概述"><a href="#Netty-概述" class="headerlink" title="Netty 概述"></a>Netty 概述</h1><h2 id="原生-NIO-存在的问题"><a href="#原生-NIO-存在的问题" class="headerlink" title="原生 NIO 存在的问题"></a>原生 NIO 存在的问题</h2><ol>
<li><code>NIO</code> 的类库和 <code>API</code> 繁杂，使用麻烦：需要熟练掌握 <code>Selector</code>、<code>ServerSocketChannel</code>、<code>SocketChannel</code>、<code>ByteBuffer</code>等。</li>
<li>需要具备其他的额外技能：要熟悉 <code>Java</code> 多线程编程，因为 <code>NIO</code> 编程涉及到 <code>Reactor</code> 模式，你必须对多线程和网络编程非常熟悉，才能编写出高质量的 <code>NIO</code> 程序。</li>
<li>开发工作量和难度都非常大：例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。</li>
<li><code>JDK NIO</code> 的 <code>Bug</code>：例如臭名昭著的 <code>Epoll Bug</code>，它会导致 <code>Selector</code> 空轮询，最终导致 <code>CPU100%</code>。直到 <code>JDK1.7</code> 版本该问题仍旧存在，没有被根本解决。</li>
</ol>
<h2 id="Netty-官网说明"><a href="#Netty-官网说明" class="headerlink" title="Netty 官网说明"></a>Netty 官网说明</h2><p>官网：<a target="_blank" rel="noopener" href="https://netty.io/">https://netty.io/</a></p>
<p>Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers &amp; clients.</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>

<h2 id="Netty-的优点"><a href="#Netty-的优点" class="headerlink" title="Netty 的优点"></a>Netty 的优点</h2><p><code>Netty</code> 对 <code>JDK</code> 自带的 <code>NIO</code> 的 <code>API</code> 进行了封装，解决了上述问题。</p>
<ol>
<li>设计优雅：适用于各种传输类型的统一 <code>API</code> 阻塞和非阻塞 <code>Socket</code>；基于灵活且可扩展的事件模型，可以清晰地分离关注点；高度可定制的线程模型-单线程，一个或多个线程池。</li>
<li>使用方便：详细记录的 <code>Javadoc</code>，用户指南和示例；没有其他依赖项，<code>JDK5（Netty3.x）</code>或 <code>6（Netty4.x）</code>就足够了。</li>
<li>高性能、吞吐量更高：延迟更低；减少资源消耗；最小化不必要的内存复制。</li>
<li>安全：完整的 <code>SSL/TLS</code> 和 <code>StartTLS</code> 支持。</li>
<li>社区活跃、不断更新：社区活跃，版本迭代周期短，发现的 <code>Bug</code> 可以被及时修复，同时，更多的新功能会被加入。</li>
</ol>
<h2 id="Netty-版本说明"><a href="#Netty-版本说明" class="headerlink" title="Netty 版本说明"></a>Netty 版本说明</h2><ol>
<li><code>Netty</code> 版本分为 <code>Netty 3.x</code> 和 <code>Netty 4.x</code>、<code>Netty 5.x</code></li>
<li>因为 <code>Netty 5</code> 出现重大 <code>bug</code>，已经被官网废弃了，目前推荐使用的是 <code>Netty 4.x</code>的稳定版本</li>
<li>目前在官网可下载的版本 <code>Netty 3.x</code>、<code>Netty 4.0.x</code> 和 <code>Netty 4.1.x</code></li>
<li>在本套课程中，我们讲解 <code>Netty4.1.x</code> 版本</li>
<li><code>Netty</code> 下载地址：<a target="_blank" rel="noopener" href="https://bintray.com/netty/downloads/netty/">https://bintray.com/netty/downloads/netty/</a></li>
</ol>
<h1 id="Netty-高性能架构设计"><a href="#Netty-高性能架构设计" class="headerlink" title="Netty 高性能架构设计"></a>Netty 高性能架构设计</h1><h2 id="线程模型基本介绍"><a href="#线程模型基本介绍" class="headerlink" title="线程模型基本介绍"></a>线程模型基本介绍</h2><ol>
<li>不同的线程模式，对程序的性能有很大影响，为了搞清 <code>Netty</code> 线程模式，我们来系统的讲解下各个线程模式，最后看看 <code>Netty</code> 线程模型有什么优越性。</li>
<li>目前存在的线程模型有：传统阻塞 <code>I/O</code> 服务模型 和<code>Reactor</code> 模式</li>
<li>根据 <code>Reactor</code> 的数量和处理资源池线程的数量不同，有 <code>3</code> 种典型的实现<ul>
<li>单 <code>Reactor</code> 单线程；</li>
<li>单 <code>Reactor</code>多线程；</li>
<li>主从 <code>Reactor</code>多线程</li>
</ul>
</li>
<li><code>Netty</code> 线程模式（<code>Netty</code> 主要基于主从 <code>Reactor</code> 多线程模型做了一定的改进，其中主从 <code>Reactor</code> 多线程模型有多个 <code>Reactor</code>）</li>
</ol>
<h2 id="传统阻塞-I-x2F-O-服务模型"><a href="#传统阻塞-I-x2F-O-服务模型" class="headerlink" title="传统阻塞 I&#x2F;O 服务模型"></a>传统阻塞 I&#x2F;O 服务模型</h2><h3 id="工作原理图"><a href="#工作原理图" class="headerlink" title="工作原理图"></a>工作原理图</h3><ol>
<li>黄色的框表示对象，蓝色的框表示线程</li>
<li>白色的框表示方法（<code>API</code>）</li>
</ol>
<h3 id="模型特点"><a href="#模型特点" class="headerlink" title="模型特点"></a>模型特点</h3><ol>
<li>采用阻塞 <code>IO</code> 模式获取输入的数据</li>
<li>每个连接都需要独立的线程完成数据的输入，业务处理，数据返回</li>
</ol>
<h3 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h3><ol>
<li>当并发数很大，就会创建大量的线程，占用很大系统资源</li>
<li>连接创建后，如果当前线程暂时没有数据可读，该线程会阻塞在 Handler对象中的<code>read</code> 操作，导致上面的处理线程资源浪费</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />

<h2 id="Reactor-模式"><a href="#Reactor-模式" class="headerlink" title="Reactor 模式"></a>Reactor 模式</h2><h3 id="针对传统阻塞-I-x2F-O-服务模型的-2-个缺点，解决方案："><a href="#针对传统阻塞-I-x2F-O-服务模型的-2-个缺点，解决方案：" class="headerlink" title="针对传统阻塞 I&#x2F;O 服务模型的 2 个缺点，解决方案："></a>针对传统阻塞 I&#x2F;O 服务模型的 2 个缺点，解决方案：</h3><p>基于 <code>I/O</code> 复用模型：多个连接共用一个阻塞对象<code>ServiceHandler</code>，应用程序只需要在一个阻塞对象等待，无需阻塞等待所有连接。当某个连接有新的数据可以处理时，操作系统通知应用程序，线程从阻塞状态返回，开始进行业务处理。</p>
<p> <code>Reactor</code> 在不同书中的叫法：</p>
<ol>
<li><p>反应器模式 </p>
</li>
<li><p>分发者模式（Dispatcher）</p>
</li>
<li><p>通知者模式（notifier）</p>
</li>
<li><p>基于线程池复用线程资源：不必再为每个连接创建线程，将连接完成后的业务处理任务分配给线程进行处理，一个线程可以处理多个连接的业务。（解决了当并发数很大时，会创建大量线程，占用很大系统资源）</p>
</li>
<li><p>基于 <code>I/O</code> 复用模型：多个客户端进行连接，先把连接请求给<code>ServiceHandler</code>。多个连接共用一个阻塞对象<code>ServiceHandler</code>。假设，当C1连接没有数据要处理时，C1客户端只需要阻塞于<code>ServiceHandler</code>，C1之前的处理线程便可以处理其他有数据的连接，不会造成线程资源的浪费。当C1连接再次有数据时，<code>ServiceHandler</code>根据线程池的空闲状态，将请求分发给空闲的线程来处理C1连接的任务。（解决了线程资源浪费的那个问题）</p>
</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>





<h3 id="I-x2F-O-复用结合线程池，就是-Reactor-模式基本设计思想，如图"><a href="#I-x2F-O-复用结合线程池，就是-Reactor-模式基本设计思想，如图" class="headerlink" title="I&#x2F;O 复用结合线程池，就是 Reactor 模式基本设计思想，如图"></a>I&#x2F;O 复用结合线程池，就是 Reactor 模式基本设计思想，如图</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>

<p>对上图说明：</p>
<ol>
<li><code>Reactor</code> 模式，通过一个或多个输入同时传递给服务处理器（ServiceHandler）的模式（基于事件驱动）</li>
<li>服务器端程序处理传入的多个请求,并将它们同步分派到相应的处理线程，因此 <code>Reactor</code> 模式也叫 <code>Dispatcher</code> 模式</li>
<li><code>Reactor</code> 模式使用 <code>IO</code> 复用监听事件，收到事件后，分发给某个线程（进程），这点就是网络服务器高并发处理关键</li>
</ol>
<blockquote>
<p>原先有多个Handler阻塞，现在只用一个ServiceHandler阻塞</p>
</blockquote>
<h3 id="Reactor-模式中核心组成"><a href="#Reactor-模式中核心组成" class="headerlink" title="Reactor 模式中核心组成"></a>Reactor 模式中核心组成</h3><ol>
<li><code>Reactor（也就是那个ServiceHandler）</code>：<code>Reactor</code> 在一个单独的线程中运行，负责监听和分发事件，分发给适当的处理线程来对 <code>IO</code> 事件做出反应。它就像公司的电话接线员，它接听来自客户的电话并将线路转移到适当的联系人；</li>
<li><code>Handlers（处理线程EventHandler）</code>：处理线程执行 <code>I/O</code> 事件要完成的实际事件，类似于客户想要与之交谈的公司中的实际官员。<code>Reactor</code> 通过调度适当的处理线程来响应 <code>I/O</code> 事件，处理程序执行非阻塞操作。</li>
</ol>
<h3 id="Reactor-模式分类"><a href="#Reactor-模式分类" class="headerlink" title="Reactor 模式分类"></a>Reactor 模式分类</h3><p>根据 <code>Reactor</code> 的数量和处理资源池线程的数量不同，有 <code>3</code> 种典型的实现</p>
<ol>
<li>单 <code>Reactor</code> 单线程</li>
<li>单 <code>Reactor</code> 多线程</li>
<li>主从 <code>Reactor</code> 多线程</li>
</ol>
<h2 id="单-Reactor-单线程"><a href="#单-Reactor-单线程" class="headerlink" title="单 Reactor 单线程"></a>单 Reactor 单线程</h2><p>原理图，并使用 <code>NIO</code> 群聊系统验证</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />

<h3 id="方案说明"><a href="#方案说明" class="headerlink" title="方案说明"></a>方案说明</h3><ol>
<li><code>Select</code> 是前面 <code>I/O</code> 复用模型介绍的标准网络编程 <code>API</code>，可以实现应用程序通过一个阻塞对象监听多路连接请求</li>
<li><code>Reactor</code> 对象通过 <code>Select</code> 监控客户端请求事件，收到事件后通过 <code>Dispatch</code> 进行分发</li>
<li>如果是建立连接请求事件，则由 <code>Acceptor</code> 通过 <code>Accept</code> 处理连接请求，然后创建一个 <code>Handler</code> 对象处理连接完成后的后续业务处理</li>
<li>如果不是建立连接事件，则 <code>Reactor</code> 会分发调用连接对应的 <code>Handler</code> 来响应</li>
<li><code>Handler</code> 会完成 <code>Read</code> → 业务处理 → <code>Send</code> 的完整业务流程</li>
</ol>
<p>结合实例：服务器端用一个线程通过多路复用搞定所有的 <code>IO</code> 操作（包括连接，读、写等），编码简单，清晰明了，但是如果客户端连接数量较多，将无法支撑，前面的 <code>NIO</code> 案例就属于这种模型。</p>
<h3 id="方案优缺点分析"><a href="#方案优缺点分析" class="headerlink" title="方案优缺点分析"></a>方案优缺点分析</h3><ol>
<li>优点：模型简单，没有多线程、进程通信、竞争的问题，全部都在一个线程中完成</li>
<li>缺点：性能问题，只有一个线程，无法完全发挥多核 <code>CPU</code> 的性能。<code>Handler</code>在处理某个连接上的业务时，整个进程无法处理其他连接事件，很容易导致性能瓶颈</li>
<li>缺点：可靠性问题，线程意外终止，或者进入死循环，会导致整个系统通信模块不可用，不能接收和处理外部消息，造成节点故障</li>
<li>使用场景：客户端的数量有限，业务处理非常快速，比如 <code>Redis</code> 在业务处理的时间复杂度 <code>O(1)</code> 的情况</li>
</ol>
<h2 id="单-Reactor-多线程"><a href="#单-Reactor-多线程" class="headerlink" title="单 Reactor 多线程"></a>单 Reactor 多线程</h2><h3 id="方案说明-1"><a href="#方案说明-1" class="headerlink" title="方案说明"></a>方案说明</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>



<ol>
<li><code>Reactor</code> 对象通过 <code>Select</code> 监控客户端请求事件，收到事件后，通过 <code>Dispatch</code> 进行分发</li>
<li>如果是建立连接请求，则由 <code>Acceptor</code> 通过 <code>accept</code> 处理连接请求，然后创建一个 <code>Handler</code> 对象处理完成连接后的各种事件</li>
<li>如果不是连接请求，则由 <code>Reactor</code> 分发调用连接对应的 <code>handler</code> 来处理（也就是说连接已经建立，后续客户端再来请求，那基本就是数据请求了，直接调用之前为这个连接创建好的handler来处理）</li>
<li><code>handler</code> 只负责响应事件，不做具体的业务处理（这样不会使handler阻塞太久），通过 <code>read</code> 读取数据后，会分发给后面的 <code>worker</code> 线程池的某个线程处理业务。【业务处理是最费时的，所以将业务处理交给线程池去执行】</li>
<li><code>worker</code> 线程池会分配独立线程完成真正的业务，并将结果返回给 <code>handler</code></li>
<li><code>handler</code> 收到响应后，通过 <code>send</code> 将结果返回给 <code>client</code></li>
</ol>
<h3 id="方案优缺点分析-1"><a href="#方案优缺点分析-1" class="headerlink" title="方案优缺点分析"></a>方案优缺点分析</h3><ol>
<li>优点：可以充分的利用多核 <code>cpu</code> 的处理能力</li>
<li>缺点：多线程数据共享和访问比较复杂。<code>Reactor</code> 承担所有的事件的监听和响应，它是单线程运行，在高并发场景容易出现性能瓶颈。也就是说<code>Reactor</code>主线程承担了过多的事</li>
</ol>
<h2 id="主从-Reactor-多线程"><a href="#主从-Reactor-多线程" class="headerlink" title="主从 Reactor 多线程"></a>主从 Reactor 多线程</h2><h3 id="工作原理图-1"><a href="#工作原理图-1" class="headerlink" title="工作原理图"></a>工作原理图</h3><p>针对单 <code>Reactor</code> 多线程模型中，<code>Reactor</code> 在单线程中运行，高并发场景下容易成为性能瓶颈，可以让 <code>Reactor</code> 在多线程中运行</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />



<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />

<blockquote>
<p>SubReactor是可以有多个的，如果只有一个SubReactor的话那和<code>单 Reactor 多线程</code>就没什么区别了。</p>
</blockquote>
<ol>
<li><code>Reactor</code> 主线程 <code>MainReactor</code> 对象通过 <code>select</code> 监听连接事件，收到事件后，通过 <code>Acceptor</code> 处理连接事件</li>
<li>当 <code>Acceptor</code> 处理连接事件后，<code>MainReactor</code> 将连接分配给 <code>SubReactor</code></li>
<li><code>subreactor</code> 将连接加入到连接队列进行监听，并创建 <code>handler</code> 进行各种事件处理</li>
<li>当有新事件发生时，<code>subreactor</code> 就会调用对应的 <code>handler</code> 处理</li>
<li><code>handler</code> 通过 <code>read</code> 读取数据，分发给后面的 <code>worker</code> 线程处理</li>
<li><code>worker</code> 线程池分配独立的 <code>worker</code> 线程进行业务处理，并返回结果</li>
<li><code>handler</code> 收到响应的结果后，再通过 <code>send</code> 将结果返回给 <code>client</code> </li>
<li><code>Reactor</code> 主线程可以对应多个 <code>Reactor</code> 子线程，即 <code>MainRecator</code> 可以关联多个 <code>SubReactor</code></li>
</ol>
<h3 id="Scalable-IO-in-Java-对-Multiple-Reactors-的原理图解"><a href="#Scalable-IO-in-Java-对-Multiple-Reactors-的原理图解" class="headerlink" title="Scalable IO in Java 对 Multiple Reactors 的原理图解"></a>Scalable IO in Java 对 Multiple Reactors 的原理图解</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>

<h3 id="方案优缺点说明"><a href="#方案优缺点说明" class="headerlink" title="方案优缺点说明"></a>方案优缺点说明</h3><ol>
<li>优点：父线程与子线程的数据交互简单职责明确，父线程只需要接收新连接，子线程完成后续的业务处理。</li>
<li>优点：父线程与子线程的数据交互简单，<code>Reactor</code> 主线程只需要把新连接传给子线程，子线程无需返回数据。</li>
<li>缺点：编程复杂度较高</li>
</ol>
<p>结合实例：这种模型在许多项目中广泛使用，包括 <code>Nginx</code> 主从 <code>Reactor</code> 多进程模型，<code>Memcached</code> 主从多线程，<code>Netty</code> 主从多线程模型的支持</p>
<h2 id="Reactor-模式小结"><a href="#Reactor-模式小结" class="headerlink" title="Reactor 模式小结"></a>Reactor 模式小结</h2><h3 id="3-种模式用生活案例来理解"><a href="#3-种模式用生活案例来理解" class="headerlink" title="3 种模式用生活案例来理解"></a>3 种模式用生活案例来理解</h3><ol>
<li>单 <code>Reactor</code> 单线程，前台接待员和服务员是同一个人，全程为顾客服</li>
<li>单 <code>Reactor</code> 多线程，<code>1</code> 个前台接待员，多个服务员，接待员只负责接待</li>
<li>主从 <code>Reactor</code> 多线程，多个前台接待员，多个服务生</li>
</ol>
<h3 id="Reactor-模式具有如下的优点"><a href="#Reactor-模式具有如下的优点" class="headerlink" title="Reactor 模式具有如下的优点"></a>Reactor 模式具有如下的优点</h3><ol>
<li>响应快，不必为单个同步时间所阻塞，虽然 <code>Reactor</code> 本身依然是同步的（比如你第一个SubReactor阻塞了，我可以调下一个 SubReactor为客户端服务）</li>
<li>可以最大程度的避免复杂的多线程及同步问题，并且避免了多线程&#x2F;进程的切换开销</li>
<li>扩展性好，可以方便的通过增加 <code>Reactor</code> 实例个数来充分利用 <code>CPU</code> 资源</li>
<li>复用性好，<code>Reactor</code> 模型本身与具体事件处理逻辑无关，具有很高的复用性</li>
</ol>
<h2 id="Netty-模型"><a href="#Netty-模型" class="headerlink" title="Netty 模型"></a>Netty 模型</h2><blockquote>
<p>讲解netty的时候采用的是先写代码体验一下，再细讲里面的原理。前面看不懂的可以先不用纠结，先往后面看，后面基本都会讲清楚</p>
</blockquote>
<h3 id="工作原理示意图1-简单版"><a href="#工作原理示意图1-简单版" class="headerlink" title="工作原理示意图1 - 简单版"></a>工作原理示意图1 - 简单版</h3><p><code>Netty</code> 主要基于主从 <code>Reactors</code> 多线程模型（如图）做了一定的改进，其中主从 <code>Reactor</code> 多线程模型有多个 <code>Reactor</code></p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>

<p><strong>对上图说明</strong></p>
<ol>
<li><code>BossGroup</code> 线程维护 <code>Selector</code>，只关注 <code>Accecpt</code> </li>
<li>当接收到 <code>Accept</code> 事件，获取到对应的 <code>SocketChannel</code>，封装成 <code>NIOScoketChannel</code> 并注册到 <code>Worker</code> 线程（事件循环），并进行维护</li>
<li>当 <code>Worker</code> 线程监听到 <code>Selector</code> 中通道发生自己感兴趣的事件后，就进行处理（就由 <code>handler</code>），注意 <code>handler</code> 已经加入到通道</li>
</ol>
<h3 id="工作原理示意图2-进阶版"><a href="#工作原理示意图2-进阶版" class="headerlink" title="工作原理示意图2 - 进阶版"></a>工作原理示意图2 - 进阶版</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>



<p><code>BossGroup</code>有点像主<code>Reactor</code> 可以有多个，<code>WorkerGroup</code>则像<code>SubReactor</code>一样可以有多个。</p>
<h3 id="工作原理示意图3-详细版"><a href="#工作原理示意图3-详细版" class="headerlink" title="工作原理示意图3 - 详细版"></a>工作原理示意图3 - 详细版</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>



<ol>
<li><code>Netty</code> 抽象出两组线程池 ，<code>BossGroup</code> 专门负责接收客户端的连接，<code>WorkerGroup</code> 专门负责网络的读写</li>
<li><code>BossGroup</code> 和 <code>WorkerGroup</code> 类型都是 <code>NioEventLoopGroup</code> </li>
<li><code>NioEventLoopGroup</code> 相当于一个事件循环组，这个组中含有多个事件循环，每一个事件循环是 <code>NioEventLoop</code> </li>
<li><code>NioEventLoop</code> 表示一个不断循环的执行处理任务的线程，每个 <code>NioEventLoop</code> 都有一个 <code>Selector</code>，用于监听绑定在其上的 <code>socket</code> 的网络通讯</li>
<li><code>NioEventLoopGroup</code> 可以有多个线程，即可以含有多个 <code>NioEventLoop</code> </li>
<li>每个 <code>BossGroup</code>下面的<code>NioEventLoop</code> 循环执行的步骤有 <code>3</code> 步<ul>
<li>轮询 <code>accept</code> 事件</li>
<li>处理 <code>accept</code> 事件，与 <code>client</code> 建立连接，生成 <code>NioScocketChannel</code>，并将其注册到某个 <code>workerGroup</code> <code>NIOEventLoop</code> 上的 <code>Selector</code></li>
<li>继续处理任务队列的任务，即 <code>runAllTasks</code></li>
</ul>
</li>
<li>每个 <code>WorkerGroup</code> <code>NIOEventLoop</code> 循环执行的步骤<ul>
<li>轮询 <code>read</code>，<code>write</code> 事件</li>
<li>处理 <code>I/O</code> 事件，即 <code>read</code>，<code>write</code> 事件，在对应 <code>NioScocketChannel</code> 处理</li>
<li>处理任务队列的任务，即 <code>runAllTasks</code></li>
</ul>
</li>
<li>每个 <code>Worker</code> <code>NIOEventLoop</code> 处理业务时，会使用 <code>pipeline</code>（管道），<code>pipeline</code> 中包含了 <code>channel（通道）</code>，即通过 <code>pipeline</code> 可以获取到对应通道，管道中维护了很多的处理器。（这个点目前只是简单的讲，后面重点说）</li>
</ol>
<h3 id="Netty-快速入门实例-TCP-服务"><a href="#Netty-快速入门实例-TCP-服务" class="headerlink" title="Netty 快速入门实例 - TCP 服务"></a>Netty 快速入门实例 - TCP 服务</h3><p>实例要求：使用 <code>IDEA</code> 创建 <code>Netty</code> 项目</p>
<ol>
<li><code>Netty</code> 服务器在 <code>6668</code> 端口监听，客户端能发送消息给服务器”hello,服务器~”</li>
<li>服务器可以回复消息给客户端”hello,客户端~”</li>
<li>目的：对 <code>Netty</code> 线程模型有一个初步认识，便于理解 <code>Netty</code> 模型理论</li>
<li><ol>
<li>编写服务端 </li>
<li>编写客户端   </li>
<li>对 <code>netty</code> 程序进行分析，看看 <code>netty</code> 模型特点</li>
<li>说明：创建 <code>Maven</code> 项目，并引入 <code>Netty</code> 包</li>
</ol>
</li>
<li>代码如下</li>
</ol>
<h4 id="NettyServer"><a href="#NettyServer" class="headerlink" title="NettyServer"></a>NettyServer</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.simple;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建BossGroup 和 WorkerGroup</span></span><br><span class="line">        <span class="comment">//说明</span></span><br><span class="line">        <span class="comment">//1. 创建两个线程组 bossGroup 和 workerGroup</span></span><br><span class="line">        <span class="comment">//2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理，会交给 workerGroup完成</span></span><br><span class="line">        <span class="comment">//3. 两个都是无限循环</span></span><br><span class="line">        <span class="comment">//4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数</span></span><br><span class="line">        <span class="comment">//   默认实际 cpu核数 * 2</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建服务器端的启动对象，配置参数</span></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//使用链式编程来进行设置</span></span><br><span class="line">            bootstrap.group(bossGroup, workerGroup) <span class="comment">//设置两个线程组</span></span><br><span class="line">                    .channel(NioServerSocketChannel.class) <span class="comment">//使用NioSocketChannel 作为服务器的通道实现</span></span><br><span class="line">                    .option(ChannelOption.SO_BACKLOG, <span class="number">128</span>) <span class="comment">// 设置线程队列等待连接个数</span></span><br><span class="line">                    .childOption(ChannelOption.SO_KEEPALIVE, <span class="literal">true</span>) <span class="comment">//设置保持活动连接状态</span></span><br><span class="line"><span class="comment">//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup</span></span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;<span class="comment">//创建一个通道初始化对象(匿名对象)</span></span><br><span class="line">                        <span class="comment">//给pipeline 设置处理器</span></span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                            System.out.println(<span class="string">&quot;客户socketchannel hashcode=&quot;</span> + ch.hashCode()); <span class="comment">//可以使用一个集合管理 SocketChannel， 再推送消息时，可以将业务加入到各个channel 对应的 NIOEventLoop 的 taskQueue 或者 scheduleTaskQueue</span></span><br><span class="line">                            ch.pipeline().addLast(<span class="keyword">new</span> <span class="title class_">NettyServerHandler</span>());</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;); <span class="comment">// 给我们的workerGroup 的 EventLoop 对应的管道设置处理器</span></span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;.....服务器 is ready...&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//绑定一个端口并且同步生成了一个 ChannelFuture 对象（也就是立马返回这样一个对象）</span></span><br><span class="line">            <span class="comment">//启动服务器(并绑定端口)</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">cf</span> <span class="operator">=</span> bootstrap.bind(<span class="number">6668</span>).sync();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//给cf 注册监听器，监控我们关心的事件</span></span><br><span class="line"></span><br><span class="line">            cf.addListener(<span class="keyword">new</span> <span class="title class_">ChannelFutureListener</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">operationComplete</span><span class="params">(ChannelFuture future)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    <span class="keyword">if</span> (cf.isSuccess()) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 成功&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 失败&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="comment">//对关闭通道事件  进行监听</span></span><br><span class="line">            cf.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="NettyServerHandler"><a href="#NettyServerHandler" class="headerlink" title="NettyServerHandler"></a>NettyServerHandler</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.simple;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.Channel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">说明</span></span><br><span class="line"><span class="comment">1. 我们自定义一个Handler 需要继承netty 规定好的某个HandlerAdapter(规范)</span></span><br><span class="line"><span class="comment">2. 这时我们自定义一个Handler , 才能称为一个handler</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取数据事件(这里我们可以读取客户端发送的消息)</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址</span></span><br><span class="line"><span class="comment">    2. Object msg: 就是客户端发送的数据 默认Object</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器读取线程 &quot;</span> + Thread.currentThread().getName() + <span class="string">&quot; channle =&quot;</span> + ctx.channel());</span><br><span class="line">        System.out.println(<span class="string">&quot;server ctx =&quot;</span> + ctx);</span><br><span class="line">        System.out.println(<span class="string">&quot;看看channel 和 pipeline的关系&quot;</span>);</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> ctx.channel();</span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ctx.pipeline(); <span class="comment">//本质是一个双向链表</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//将 msg 转成一个 ByteBuf</span></span><br><span class="line">        <span class="comment">//ByteBuf 是 Netty 提供的，不是 NIO 的 ByteBuffer.</span></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buf</span> <span class="operator">=</span> (ByteBuf) msg;</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端发送消息是:&quot;</span> + buf.toString(CharsetUtil.UTF_8));</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端地址:&quot;</span> + channel.remoteAddress());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//数据读取完毕</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelReadComplete</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//writeAndFlush 是 write + flush</span></span><br><span class="line">        <span class="comment">//将数据写入到缓存，并刷新</span></span><br><span class="line">        <span class="comment">//一般讲，我们对这个发送的数据进行编码</span></span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵1&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//发生异常后, 一般是需要关闭通道</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="NettyClient"><a href="#NettyClient" class="headerlink" title="NettyClient"></a>NettyClient</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.simple;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//客户端需要一个事件循环组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建客户端启动对象</span></span><br><span class="line">            <span class="comment">//注意客户端使用的不是 ServerBootstrap 而是 Bootstrap</span></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//设置相关参数</span></span><br><span class="line">            bootstrap.group(group) <span class="comment">//设置线程组</span></span><br><span class="line">                    .channel(NioSocketChannel.class) <span class="comment">// 设置客户端通道的实现类(反射)</span></span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                            ch.pipeline().addLast(<span class="keyword">new</span> <span class="title class_">NettyClientHandler</span>()); <span class="comment">//加入自己的处理器</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;客户端 ok..&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//启动客户端去连接服务器端</span></span><br><span class="line">            <span class="comment">//关于 ChannelFuture 要分析，涉及到netty的异步模型</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6668</span>).sync();</span><br><span class="line">            <span class="comment">//对关闭通道事件  进行监听</span></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line"></span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="NettyClientHandler"><a href="#NettyClientHandler" class="headerlink" title="NettyClientHandler"></a>NettyClientHandler</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.simple;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道就绪就会触发该方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;client &quot;</span> + ctx);</span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, server: (&gt;^ω^&lt;)喵&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道有读取事件时，会触发</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buf</span> <span class="operator">=</span> (ByteBuf) msg;</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器回复的消息:&quot;</span> + buf.toString(CharsetUtil.UTF_8));</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器的地址： &quot;</span>+ ctx.channel().remoteAddress());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        cause.printStackTrace();</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="任务队列中的-Task-有-3-种典型使用场景"><a href="#任务队列中的-Task-有-3-种典型使用场景" class="headerlink" title="任务队列中的 Task 有 3 种典型使用场景"></a>任务队列中的 Task 有 3 种典型使用场景</h3><ol>
<li>用户程序自定义的普通任务【举例说明】</li>
<li>用户自定义定时任务</li>
<li>非当前 <code>Reactor</code> 线程调用 <code>Channel</code> 的各种方法<br>例如在<strong>推送系统</strong>的业务线程里面，根据用户的标识，找到对应的 <code>Channel</code> 引用，然后调用 <code>Write</code> 类方法向该用户推送消息，就会进入到这种场景。最终的 <code>Write</code> 会提交到任务队列中后被异步消费</li>
</ol>
<p>前两种的代码举例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.simple;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 说明</span></span><br><span class="line"><span class="comment"> * 1. 我们自定义一个Handler 需要继续netty 规定好的某个HandlerAdapter(规范)</span></span><br><span class="line"><span class="comment"> * 2. 这时我们自定义一个Handler , 才能称为一个handler</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取数据实际(这里我们可以读取客户端发送的消息)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址</span></span><br><span class="line"><span class="comment">     * 2. Object msg: 就是客户端发送的数据 默认Object</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 比如这里我们有一个非常耗时长的业务-&gt; 异步执行 -&gt; 提交该channel 对应的</span></span><br><span class="line">        <span class="comment">// NIOEventLoop 的 taskQueue中,</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解决方案1 用户程序自定义的普通任务</span></span><br><span class="line"></span><br><span class="line">        ctx.channel().eventLoop().execute(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">5</span> * <span class="number">1000</span>);</span><br><span class="line">                    ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵2&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">                    System.out.println(<span class="string">&quot;channel code=&quot;</span> + ctx.channel().hashCode());</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;发生异常&quot;</span> + ex.getMessage());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        ctx.channel().eventLoop().execute(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">5</span> * <span class="number">1000</span>);</span><br><span class="line">                    ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵3&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">                    System.out.println(<span class="string">&quot;channel code=&quot;</span> + ctx.channel().hashCode());</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;发生异常&quot;</span> + ex.getMessage());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//解决方案2 : 用户自定义定时任务 -》 该任务是提交到 scheduleTaskQueue中</span></span><br><span class="line"></span><br><span class="line">        ctx.channel().eventLoop().schedule(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">5</span> * <span class="number">1000</span>);</span><br><span class="line">                    ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵4&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">                    System.out.println(<span class="string">&quot;channel code=&quot;</span> + ctx.channel().hashCode());</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;发生异常&quot;</span> + ex.getMessage());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="number">5</span>, TimeUnit.SECONDS);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;go on ...&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//        System.out.println(&quot;服务器读取线程 &quot; + Thread.currentThread().getName() + &quot; channle =&quot; + ctx.channel());</span></span><br><span class="line"><span class="comment">//        System.out.println(&quot;server ctx =&quot; + ctx);</span></span><br><span class="line"><span class="comment">//        System.out.println(&quot;看看channel 和 pipeline的关系&quot;);</span></span><br><span class="line"><span class="comment">//        Channel channel = ctx.channel();</span></span><br><span class="line"><span class="comment">//        ChannelPipeline pipeline = ctx.pipeline(); //本质是一个双向链接, 出站入站</span></span><br><span class="line"><span class="comment">//        </span></span><br><span class="line"><span class="comment">//        //将 msg 转成一个 ByteBuf</span></span><br><span class="line"><span class="comment">//        //ByteBuf 是 Netty 提供的，不是 NIO 的 ByteBuffer.</span></span><br><span class="line"><span class="comment">//        ByteBuf buf = (ByteBuf) msg;</span></span><br><span class="line"><span class="comment">//        System.out.println(&quot;客户端发送消息是:&quot; + buf.toString(CharsetUtil.UTF_8));</span></span><br><span class="line"><span class="comment">//        System.out.println(&quot;客户端地址:&quot; + channel.remoteAddress());</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//数据读取完毕</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelReadComplete</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//writeAndFlush 是 write + flush</span></span><br><span class="line">        <span class="comment">//将数据写入到缓存，并刷新</span></span><br><span class="line">        <span class="comment">//一般讲，我们对这个发送的数据进行编码</span></span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵1&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理异常, 一般是需要关闭通道</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="方案再说明"><a href="#方案再说明" class="headerlink" title="方案再说明"></a>方案再说明</h3><ol>
<li><code>Netty</code> 抽象出两组线程池，<code>BossGroup</code> 专门负责接收客户端连接，<code>WorkerGroup</code> 专门负责网络读写操作。</li>
<li><code>NioEventLoop</code> 表示一个不断循环执行处理任务的线程，每个 <code>NioEventLoop</code> 都有一个 <code>Selector</code>，用于监听绑定在其上的 <code>socket</code>网络通道。</li>
<li><code>NioEventLoop</code> 内部采用串行化设计，从消息的 <strong>读取-&gt;解码-&gt;处理-&gt;编码-&gt;发送</strong>，始终由 <code>IO</code> 线程 <code>NioEventLoop</code> 负责</li>
</ol>
<ul>
<li><p><code>NioEventLoopGroup</code> 下包含多个 <code>NioEventLoop</code></p>
</li>
<li><p>每个 <code>NioEventLoop</code> 中包含有一个 <code>Selector</code>，一个 <code>taskQueue</code></p>
</li>
<li><p>每个 <code>NioEventLoop</code> 的 <code>Selector</code> 上可以注册监听多个 <code>NioChannel</code></p>
</li>
<li><p>每个 <code>NioChannel</code> 只会绑定在唯一的 <code>NioEventLoop</code> 上</p>
</li>
<li><p>每个 <code>NioChannel</code> 都绑定有一个自己的 <code>ChannelPipeline</code></p>
</li>
</ul>
<h2 id="异步模型"><a href="#异步模型" class="headerlink" title="异步模型"></a>异步模型</h2><h3 id="基本介绍"><a href="#基本介绍" class="headerlink" title="基本介绍"></a>基本介绍</h3><ol>
<li>异步的概念和同步相对。当一个异步过程调用发出后，调用者不能立刻得到结果。实际处理这个调用的组件在完成后，通过状态、通知和回调来通知调用者。</li>
<li><code>Netty</code> 中的 <code>I/O</code> 操作是异步的，包括 <code>Bind、Write、Connect</code> 等操作会首先简单的返回一个 <code>ChannelFuture</code>。</li>
<li>调用者并不能立刻获得结果，而是通过 <code>Future-Listener</code> 机制，用户可以方便的主动获取或者通过通知机制获得 <code>IO</code> 操作结果。</li>
<li><code>Netty</code> 的异步模型是建立在 <code>future</code> 和 <code>callback</code> 的之上的。<code>callback</code> 就是回调。重点说 <code>Future</code>，它的核心思想是：假设一个方法 <code>fun</code>，计算过程可能非常耗时，等待 <code>fun</code> 返回显然不合适。那么可以在调用 <code>fun</code> 的时候，立马返回一个 <code>Future</code>，后续可以通过 <code>Future</code> 去监控方法 <code>fun</code> 的处理过程（即：<code>Future-Listener</code> 机制）</li>
</ol>
<h3 id="Future-说明"><a href="#Future-说明" class="headerlink" title="Future 说明"></a>Future 说明</h3><ol>
<li>表示异步的执行结果,可以通过它提供的方法来检测执行是否完成，比如检索计算等等。</li>
<li><code>ChannelFuture</code> 是一个接口：<code>public interface ChannelFuture extends Future&lt;Void&gt;</code> 我们可以添加监听器，当监听的事件发生时，就会通知到监听器。</li>
</ol>
<h3 id="工作原理示意图"><a href="#工作原理示意图" class="headerlink" title="工作原理示意图"></a>工作原理示意图</h3><p>下面第一张图就是管道，中间会经过多个handler</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>

<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>

<p>说明：</p>
<ol>
<li>在使用 <code>Netty</code> 进行编程时，拦截操作和转换出入站数据只需要您提供 <code>callback</code> 或利用 <code>future</code> 即可。这使得链式操作简单、高效，并有利于编写可重用的、通用的代码。</li>
<li><code>Netty</code> 框架的目标就是让你的业务逻辑从网络基础应用编码中分离出来、解脱出来。</li>
</ol>
<h3 id="Future-Listener-机制"><a href="#Future-Listener-机制" class="headerlink" title="Future-Listener 机制"></a>Future-Listener 机制</h3><blockquote>
<p>这里看不懂的可以看笔者的<strong>并发系列-JUC部分</strong></p>
</blockquote>
<ol>
<li>当 <code>Future</code> 对象刚刚创建时，处于非完成状态，调用者可以通过返回的 <code>ChannelFuture</code> 来获取操作执行的状态，注册监听函数来执行完成后的操作。</li>
<li>常见有如下操作<ul>
<li>通过 <code>isDone</code> 方法来判断当前操作是否完成；</li>
<li>通过 <code>isSuccess</code> 方法来判断已完成的当前操作是否成功；</li>
<li>通过 <code>getCause</code> 方法来获取已完成的当前操作失败的原因；</li>
<li>通过 <code>isCancelled</code> 方法来判断已完成的当前操作是否被取消；</li>
<li>通过 <code>addListener</code> 方法来注册监听器，当操作已完成（<code>isDone</code>方法返回完成），将会通知指定的监听器；如果 <code>Future</code> 对象已完成，则通知指定的监听器</li>
</ul>
</li>
</ol>
<p>举例说明<br>演示：绑定端口是异步操作，当绑定操作处理完，将会调用相应的监听器处理逻辑</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//绑定一个端口并且同步,生成了一个ChannelFuture对象</span></span><br><span class="line"><span class="comment">//启动服务器(并绑定端口)</span></span><br><span class="line"><span class="type">ChannelFuture</span> <span class="variable">cf</span> <span class="operator">=</span> bootstrap.bind(<span class="number">6668</span>).sync();</span><br><span class="line"><span class="comment">//给cf注册监听器，监控我们关心的事件</span></span><br><span class="line">cf.addListener(<span class="keyword">new</span> <span class="title class_">ChannelFutureListener</span>() &#123;</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">operationComplete</span> <span class="params">(ChannelFuture future)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">      <span class="keyword">if</span> (cf.isSuccess()) &#123;</span><br><span class="line">         System.out.println(<span class="string">&quot;监听端口6668成功&quot;</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         System.out.println(<span class="string">&quot;监听端口6668失败&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="快速入门实例-HTTP服务"><a href="#快速入门实例-HTTP服务" class="headerlink" title="快速入门实例 - HTTP服务"></a>快速入门实例 - HTTP服务</h2><ol>
<li>实例要求：使用 <code>IDEA</code> 创建 <code>Netty</code> 项目</li>
<li><code>Netty</code> 服务器在 <code>6668</code> 端口监听，浏览器发出请求 <code>http://localhost:6668/</code> </li>
<li>服务器可以回复消息给客户端”Hello!我是服务器5”,并对特定请求资源进行过滤。</li>
<li>目的：<code>Netty</code> 可以做 <code>Http</code> 服务开发，并且理解 <code>Handler</code> 实例和客户端及其请求的关系。</li>
<li>看老师代码演示</li>
</ol>
<h3 id="TestServer"><a href="#TestServer" class="headerlink" title="TestServer"></a>TestServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.http;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(<span class="keyword">new</span> <span class="title class_">TestServerInitializer</span>());</span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">6668</span>).sync();</span><br><span class="line">            </span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="TestServerInitializer"><a href="#TestServerInitializer" class="headerlink" title="TestServerInitializer"></a>TestServerInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.http;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.HttpServerCodec;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestServerInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向管道加入处理器</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//得到管道</span></span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//加入一个netty 提供的httpServerCodec codec =&gt;[coder - decoder]</span></span><br><span class="line">        <span class="comment">//HttpServerCodec 说明</span></span><br><span class="line">        <span class="comment">//1. HttpServerCodec 是netty 提供的处理http的 编-解码器</span></span><br><span class="line">        pipeline.addLast(<span class="string">&quot;MyHttpServerCodec&quot;</span>,<span class="keyword">new</span> <span class="title class_">HttpServerCodec</span>());</span><br><span class="line">        <span class="comment">//2. 增加一个自定义的handler</span></span><br><span class="line">        pipeline.addLast(<span class="string">&quot;MyTestHttpServerHandler&quot;</span>, <span class="keyword">new</span> <span class="title class_">TestHttpServerHandler</span>());</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;ok~~~~&quot;</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="TestHttpServerHandler"><a href="#TestHttpServerHandler" class="headerlink" title="TestHttpServerHandler"></a>TestHttpServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.http;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.net.URI;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">说明</span></span><br><span class="line"><span class="comment">1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter</span></span><br><span class="line"><span class="comment">2. HttpObject 客户端和服务器端相互通讯的数据被封装成 HttpObject</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestHttpServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;HttpObject&gt; &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//channelRead0 读取客户端数据</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, HttpObject msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;对应的channel=&quot;</span> + ctx.channel() + <span class="string">&quot; pipeline=&quot;</span> + ctx</span><br><span class="line">        .pipeline() + <span class="string">&quot; 通过pipeline获取channel&quot;</span> + ctx.pipeline().channel());</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;当前ctx的handler=&quot;</span> + ctx.handler());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//判断 msg 是不是 httprequest请求</span></span><br><span class="line">        <span class="keyword">if</span>(msg <span class="keyword">instanceof</span> HttpRequest) &#123;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;ctx 类型=&quot;</span>+ctx.getClass());</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;pipeline hashcode&quot;</span> + ctx.pipeline().hashCode() + <span class="string">&quot; TestHttpServerHandler hash=&quot;</span> + <span class="built_in">this</span>.hashCode());</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;msg 类型=&quot;</span> + msg.getClass());</span><br><span class="line">            System.out.println(<span class="string">&quot;客户端地址&quot;</span> + ctx.channel().remoteAddress());</span><br><span class="line"></span><br><span class="line">            <span class="comment">//获取到</span></span><br><span class="line">            <span class="type">HttpRequest</span> <span class="variable">httpRequest</span> <span class="operator">=</span> (HttpRequest) msg;</span><br><span class="line">            <span class="comment">//获取uri, 过滤指定的资源</span></span><br><span class="line">            <span class="type">URI</span> <span class="variable">uri</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">URI</span>(httpRequest.uri());</span><br><span class="line">            <span class="keyword">if</span>(<span class="string">&quot;/favicon.ico&quot;</span>.equals(uri.getPath())) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;请求了 favicon.ico, 不做响应&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//回复信息给浏览器 [http协议]</span></span><br><span class="line"></span><br><span class="line">            <span class="type">ByteBuf</span> <span class="variable">content</span> <span class="operator">=</span> Unpooled.copiedBuffer(<span class="string">&quot;hello, 我是服务器&quot;</span>, CharsetUtil.UTF_8);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//构造一个http的相应，即 httpresponse</span></span><br><span class="line">            <span class="type">FullHttpResponse</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultFullHttpResponse</span>(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);</span><br><span class="line"></span><br><span class="line">            response.headers().set(HttpHeaderNames.CONTENT_TYPE, <span class="string">&quot;text/plain&quot;</span>);</span><br><span class="line">            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());</span><br><span class="line"></span><br><span class="line">            <span class="comment">//将构建好 response返回</span></span><br><span class="line">            ctx.writeAndFlush(response);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="Netty-核心模块组件"><a href="#Netty-核心模块组件" class="headerlink" title="Netty 核心模块组件"></a>Netty 核心模块组件</h1><blockquote>
<p>各种东西看不懂，可以先看第三话，第三话我自认为用通俗的语言讲的还算清楚。</p>
</blockquote>
<h2 id="Bootstrap、ServerBootstrap"><a href="#Bootstrap、ServerBootstrap" class="headerlink" title="Bootstrap、ServerBootstrap"></a>Bootstrap、ServerBootstrap</h2><ol>
<li><code>Bootstrap</code> 意思是引导，一个 <code>Netty</code> 应用通常由一个 <code>Bootstrap</code> 开始，主要作用是配置整个 <code>Netty</code> 程序，串联各个组件，<code>Netty</code> 中 <code>Bootstrap</code> 类是客户端程序的启动引导类，<code>ServerBootstrap</code> 是服务端启动引导类。</li>
<li>常见的方法有<ul>
<li><code>public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)</code>，该方法用于服务器端，用来设置两个 <code>EventLoop</code></li>
<li><code>public B group(EventLoopGroup group)</code>，该方法用于客户端，用来设置一个 <code>EventLoop</code></li>
<li><code>public B channel(Class&lt;? extends C&gt; channelClass)</code>，该方法用来设置一个服务器端的通道实现</li>
<li><code>public &lt;T&gt; B option(ChannelOption&lt;T&gt; option, T value)</code>，用来给 <code>ServerChannel</code> 添加配置</li>
<li><code>public &lt;T&gt; ServerBootstrap childOption(ChannelOption&lt;T&gt; childOption, T value)</code>，用来给接收到的通道添加配置</li>
<li><code>public ServerBootstrap childHandler(ChannelHandler childHandler)</code>，该方法用来设置业务处理类（自定义的<code>handler</code>）</li>
<li><code>public ChannelFuture bind(int inetPort)</code>，该方法用于服务器端，用来设置占用的端口号</li>
<li><code>public ChannelFuture connect(String inetHost, int inetPort)</code>，该方法用于客户端，用来连接服务器端</li>
</ul>
</li>
</ol>
<h2 id="Future、ChannelFuture"><a href="#Future、ChannelFuture" class="headerlink" title="Future、ChannelFuture"></a>Future、ChannelFuture</h2><p><code>Netty</code> 中所有的 <code>IO</code> 操作都是异步的，不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听，具体的实现就是通过 <code>Future</code> 和 <code>ChannelFutures</code>，他们可以注册一个监听，当操作执行成功或失败时监听会自动触发注册的监听事件</p>
<p>常见的方法有</p>
<ul>
<li><code>Channel channel()</code>，返回当前正在进行 <code>IO</code> 操作的通道</li>
<li><code>ChannelFuture sync()</code>，等待异步操作执行完毕</li>
</ul>
<h2 id="Channel"><a href="#Channel" class="headerlink" title="Channel"></a>Channel</h2><ol>
<li><code>Netty</code> 网络通信的组件，能够用于执行网络 <code>I/O</code> 操作。</li>
<li>通过 <code>Channel</code> 可获得当前网络连接的通道的状态</li>
<li>通过 <code>Channel</code> 可获得网络连接的配置参数（例如接收缓冲区大小）</li>
<li><code>Channel</code> 提供异步的网络 <code>I/O</code> 操作(如建立连接，读写，绑定端口)，异步调用意味着任何 <code>I/O</code> 调用都将立即返回，并且不保证在调用结束时所请求的 <code>I/O</code> 操作已完成</li>
<li>调用立即返回一个 <code>ChannelFuture</code> 实例，通过注册监听器到 <code>ChannelFuture</code> 上，可以 <code>I/O</code> 操作成功、失败或取消时回调通知调用方</li>
<li>支持关联 <code>I/O</code> 操作与对应的处理程序</li>
<li>不同协议、不同的阻塞类型的连接都有不同的 <code>Channel</code> 类型与之对应，常用的 <code>Channel</code> 类型：<ul>
<li><code>NioSocketChannel</code>，异步的客户端 <code>TCP</code> <code>Socket</code> 连接。</li>
<li><code>NioServerSocketChannel</code>，异步的服务器端 <code>TCP</code> <code>Socket</code> 连接。</li>
<li><code>NioDatagramChannel</code>，异步的 <code>UDP</code> 连接。</li>
<li><code>NioSctpChannel</code>，异步的客户端 <code>Sctp</code> 连接。</li>
<li><code>NioSctpServerChannel</code>，异步的 <code>Sctp</code> 服务器端连接，这些通道涵盖了 <code>UDP</code> 和 <code>TCP</code> 网络 <code>IO</code> 以及文件 <code>IO</code>。</li>
</ul>
</li>
</ol>
<h2 id="Selector"><a href="#Selector" class="headerlink" title="Selector"></a>Selector</h2><ol>
<li><code>Netty</code> 基于 <code>Selector</code> 对象实现 <code>I/O</code> 多路复用，通过 <code>Selector</code> 一个线程可以监听多个连接的 <code>Channel</code> 事件。</li>
<li>当向一个 <code>Selector</code> 中注册 <code>Channel</code> 后，<code>Selector</code> 内部的机制就可以自动不断地查询（<code>Select</code>）这些注册的 <code>Channel</code> 是否有已就绪的 <code>I/O</code> 事件（例如可读，可写，网络连接完成等），这样程序就可以很简单地使用一个线程高效地管理多个 <code>Channel</code></li>
</ol>
<h2 id="ChannelHandler-及其实现类"><a href="#ChannelHandler-及其实现类" class="headerlink" title="ChannelHandler 及其实现类"></a>ChannelHandler 及其实现类</h2><ol>
<li><code>ChannelHandler</code> 是一个接口，处理 <code>I/O</code> 事件或拦截 <code>I/O</code> 操作，并将其转发到其 <code>ChannelPipeline</code>（业务处理链）中的下一个处理程序。</li>
<li><code>ChannelHandler</code> 本身并没有提供很多方法，因为这个接口有许多的方法需要实现，方便使用期间，可以继承它的子类</li>
<li><code>ChannelHandler</code> 及其实现类一览图（后）</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>

<ol start="4">
<li>我们经常需要自定义一个 <code>Handler</code> 类去继承 <code>ChannelInboundHandlerAdapter</code>，然后通过重写相应方法实现业务逻辑，我们接下来看看一般都需要重写哪些方法</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>

<h2 id="Pipeline-和-ChannelPipeline"><a href="#Pipeline-和-ChannelPipeline" class="headerlink" title="Pipeline 和 ChannelPipeline"></a>Pipeline 和 ChannelPipeline</h2><p><code>ChannelPipeline</code> 是一个重点：</p>
<ol>
<li><code>ChannelPipeline</code> 是一个 <code>Handler</code> 的集合，它负责处理和拦截 <code>inbound</code> 或者 <code>outbound</code> 的事件和操作，相当于一个贯穿 <code>Netty</code> 的链。（也可以这样理解：<code>ChannelPipeline</code> 是保存 <code>ChannelHandler</code> 的 <code>List</code>，用于处理或拦截 <code>Channel</code> 的入站事件和出站操作）</li>
<li><code>ChannelPipeline</code> 实现了一种高级形式的拦截过滤器模式，使用户可以完全控制事件的处理方式，以及 <code>Channel</code> 中各个的 <code>ChannelHandler</code> 如何相互交互</li>
<li>在 <code>Netty</code> 中每个 <code>Channel</code> 都有且仅有一个 <code>ChannelPipeline</code> 与之对应，它们的组成关系如下</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>

<ol start="4">
<li>常用方法<br><code>ChannelPipeline addFirst(ChannelHandler... handlers)</code>，把一个业务处理类（<code>handler</code>）添加到链中的第一个位置<code>ChannelPipeline addLast(ChannelHandler... handlers)</code>，把一个业务处理类（<code>handler</code>）添加到链中的最后一个位置</li>
</ol>
<blockquote>
<p>想要更清楚的了解pipeline，可以对之前的代码进行debug，看一下pipeline里究竟有什么东西。</p>
</blockquote>
<p>从head看一下debug</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>

<ul>
<li><code>TestServerInitializer</code>和<code>HttpServerCodec</code>这些东西本身也是<code>handler</code></li>
<li>一般来说事件从客户端往服务器走我们称为出站，反之则是入站。</li>
</ul>
<h2 id="ChannelHandlerContext"><a href="#ChannelHandlerContext" class="headerlink" title="ChannelHandlerContext"></a>ChannelHandlerContext</h2><ol>
<li>保存 <code>Channel</code> 相关的所有上下文信息，同时关联一个 <code>ChannelHandler</code> 对象</li>
<li>即 <code>ChannelHandlerContext</code> 中包含一个具体的事件处理器 <code>ChannelHandler</code>，同时 <code>ChannelHandlerContext</code> 中也绑定了对应的 <code>pipeline</code> 和 <code>Channel</code> 的信息，方便对 <code>ChannelHandler</code> 进行调用。</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>

<ol start="3">
<li>常用方法</li>
</ol>
<ul>
<li><code>ChannelFuture close()</code>，关闭通道</li>
<li><code>ChannelOutboundInvoker flush()</code>，刷新</li>
<li><code>ChannelFuture writeAndFlush(Object msg)</code>，将数据写到 </li>
<li><code>ChannelPipeline</code> 中当前 <code>ChannelHandler</code> 的下一个 <code>ChannelHandler</code> 开始处理（出站）</li>
</ul>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>

<h2 id="ChannelOption"><a href="#ChannelOption" class="headerlink" title="ChannelOption"></a>ChannelOption</h2><ol>
<li><code>Netty</code> 在创建 <code>Channel</code> 实例后，一般都需要设置 <code>ChannelOption</code> 参数。</li>
<li><code>ChannelOption</code> 参数如下：</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>

<h2 id="EventLoopGroup-和其实现类-NioEventLoopGroup"><a href="#EventLoopGroup-和其实现类-NioEventLoopGroup" class="headerlink" title="EventLoopGroup 和其实现类 NioEventLoopGroup"></a>EventLoopGroup 和其实现类 NioEventLoopGroup</h2><ol>
<li><code>EventLoopGroup</code> 是一组 <code>EventLoop</code> 的抽象，<code>Netty</code> 为了更好的利用多核 <code>CPU</code> 资源，一般会有多个 <code>EventLoop</code> 同时工作，每个 <code>EventLoop</code> 维护着一个 <code>Selector</code> 实例。</li>
<li><code>EventLoopGroup</code> 提供 <code>next</code> 接口，可以从组里面按照一定规则获取其中一个 <code>EventLoop</code> 来处理任务。在 <code>Netty</code> 服务器端编程中，我们一般都需要提供两个 <code>EventLoopGroup</code>，例如：<code>BossEventLoopGroup</code> 和 <code>WorkerEventLoopGroup</code>。</li>
<li>通常一个服务端口即一个 <code>ServerSocketChannel</code> 对应一个 <code>Selector</code> 和一个 <code>EventLoop</code> 线程。<code>BossEventLoop</code> 负责接收客户端的连接并将 <code>SocketChannel</code> 交给 <code>WorkerEventLoopGroup</code> 来进行 <code>IO</code> 处理，如下图所示</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>

<ol start="4">
<li>常用方法<br><code>public NioEventLoopGroup()</code>，构造方法<br><code>public Future&lt;?&gt; shutdownGracefully()</code>，断开连接，关闭线程</li>
</ol>
<h2 id="Unpooled-类"><a href="#Unpooled-类" class="headerlink" title="Unpooled 类"></a>Unpooled 类</h2><ol>
<li><code>Netty</code> 提供一个专门用来操作缓冲区（即 <code>Netty</code> 的数据容器）的工具类</li>
<li>常用方法如下所示</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>

<ol start="3">
<li>举例说明 <code>Unpooled</code> 获取 <code>Netty</code> 的数据容器 <code>ByteBuf</code> 的基本使用</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>

<p>案例 1</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.buf;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyByteBuf01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//创建一个ByteBuf</span></span><br><span class="line">        <span class="comment">//说明</span></span><br><span class="line">        <span class="comment">//1. 创建 对象，该对象包含一个数组arr , 是一个byte[10]</span></span><br><span class="line">        <span class="comment">//2. 在netty 的buffer中，不需要使用flip 进行反转</span></span><br><span class="line">        <span class="comment">//   底层维护了 readerindex 和 writerIndex</span></span><br><span class="line">        <span class="comment">//3. 通过 readerindex 和  writerIndex 和  capacity， 将buffer分成三个区域</span></span><br><span class="line">        <span class="comment">// 0---readerindex 已经读取的区域</span></span><br><span class="line">        <span class="comment">// readerindex---writerIndex ， 可读的区域</span></span><br><span class="line">        <span class="comment">// writerIndex -- capacity, 可写的区域</span></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buffer</span> <span class="operator">=</span> Unpooled.buffer(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            buffer.writeByte(i);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;capacity=&quot;</span> + buffer.capacity());<span class="comment">//10</span></span><br><span class="line">        <span class="comment">//输出</span></span><br><span class="line"><span class="comment">//        for(int i = 0; i&lt;buffer.capacity(); i++) &#123;</span></span><br><span class="line">        	  <span class="comment">//这个方法readerindex不会变</span></span><br><span class="line"><span class="comment">//            System.out.println(buffer.getByte(i));</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; buffer.capacity(); i++) &#123;</span><br><span class="line">            <span class="comment">//这个方法readerindex会变</span></span><br><span class="line">            System.out.println(buffer.readByte());</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;执行完毕&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>案例 2</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.buf;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyByteBuf02</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建ByteBuf</span></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">byteBuf</span> <span class="operator">=</span> Unpooled.copiedBuffer(<span class="string">&quot;hello,world!&quot;</span>, Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//使用相关的方法</span></span><br><span class="line">        <span class="keyword">if</span> (byteBuf.hasArray()) &#123; <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">            <span class="type">byte</span>[] content = byteBuf.array();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//将 content 转成字符串</span></span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(content, Charset.forName(<span class="string">&quot;utf-8&quot;</span>)));</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;byteBuf=&quot;</span> + byteBuf);</span><br><span class="line"></span><br><span class="line">            System.out.println(byteBuf.arrayOffset()); <span class="comment">// 0</span></span><br><span class="line">            System.out.println(byteBuf.readerIndex()); <span class="comment">// 0</span></span><br><span class="line">            System.out.println(byteBuf.writerIndex()); <span class="comment">// 12</span></span><br><span class="line">            System.out.println(byteBuf.capacity()); <span class="comment">// 36</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//System.out.println(byteBuf.readByte()); //</span></span><br><span class="line">            System.out.println(byteBuf.getByte(<span class="number">0</span>)); <span class="comment">// 104</span></span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> byteBuf.readableBytes(); <span class="comment">//可读的字节数  12</span></span><br><span class="line">            System.out.println(<span class="string">&quot;len=&quot;</span> + len);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//使用for取出各个字节</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">                System.out.println((<span class="type">char</span>) byteBuf.getByte(i));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//按照某个范围读取</span></span><br><span class="line">            System.out.println(byteBuf.getCharSequence(<span class="number">0</span>, <span class="number">4</span>, Charset.forName(<span class="string">&quot;utf-8&quot;</span>)));</span><br><span class="line">            System.out.println(byteBuf.getCharSequence(<span class="number">4</span>, <span class="number">6</span>, Charset.forName(<span class="string">&quot;utf-8&quot;</span>)));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Netty-应用实例-群聊系统"><a href="#Netty-应用实例-群聊系统" class="headerlink" title="Netty 应用实例-群聊系统"></a>Netty 应用实例-群聊系统</h2><p>实例要求：</p>
<ol>
<li>编写一个 <code>Netty</code> 群聊系统，实现服务器端和客户端之间的数据简单通讯（非阻塞）</li>
<li>实现多人群聊</li>
<li>服务器端：可以监测用户上线，离线，并实现消息转发功能</li>
<li>客户端：通过 <code>channel</code> 可以无阻塞发送消息给其它所有用户，同时可以接受其它用户发送的消息（有服务器转发得到）</li>
<li>目的：进一步理解 <code>Netty</code> 非阻塞网络编程机制</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>

<p>代码如下：</p>
<h3 id="GroupChatServer"><a href="#GroupChatServer" class="headerlink" title="GroupChatServer"></a>GroupChatServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.groupchat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GroupChatServer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> port; <span class="comment">//监听端口</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GroupChatServer</span><span class="params">(<span class="type">int</span> port)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.port = port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//编写run方法，处理客户端的请求</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> <span class="keyword">throws</span>  Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建两个线程组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8个NioEventLoop</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            b.group(bossGroup, workerGroup)</span><br><span class="line">                    .channel(NioServerSocketChannel.class)</span><br><span class="line">                    .option(ChannelOption.SO_BACKLOG, <span class="number">128</span>)</span><br><span class="line">                    .childOption(ChannelOption.SO_KEEPALIVE, <span class="literal">true</span>)</span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">                            <span class="comment">//获取到pipeline</span></span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            <span class="comment">//向pipeline加入解码器</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;decoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">StringDecoder</span>());</span><br><span class="line">                            <span class="comment">//向pipeline加入编码器</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;encoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">StringEncoder</span>());</span><br><span class="line">                            <span class="comment">//加入自己的业务处理handler</span></span><br><span class="line">                            pipeline.addLast(<span class="keyword">new</span> <span class="title class_">GroupChatServerHandler</span>());</span><br><span class="line"></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;netty 服务器启动&quot;</span>);</span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> b.bind(port).sync();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//监听关闭</span></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">GroupChatServer</span>(<span class="number">7000</span>).run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="GroupChatServerHandler"><a href="#GroupChatServerHandler" class="headerlink" title="GroupChatServerHandler"></a>GroupChatServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.groupchat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.Channel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.group.ChannelGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.group.DefaultChannelGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.concurrent.GlobalEventExecutor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GroupChatServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;String&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这样写还要自己遍历Channel</span></span><br><span class="line">    <span class="comment">//public static List&lt;Channel&gt; channels = new ArrayList&lt;Channel&gt;();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//使用一个hashmap 管理私聊（私聊本案例并未实现，只是提供个思路）</span></span><br><span class="line">    <span class="comment">//public static Map&lt;String, Channel&gt; channels = new HashMap&lt;String,Channel&gt;();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义一个channle 组，管理所有的channel</span></span><br><span class="line">    <span class="comment">//GlobalEventExecutor.INSTANCE) 是全局的事件执行器，是一个单例</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">ChannelGroup</span>  <span class="variable">channelGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultChannelGroup</span>(GlobalEventExecutor.INSTANCE);</span><br><span class="line">    <span class="type">SimpleDateFormat</span> <span class="variable">sdf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//handlerAdded 表示连接建立，一旦连接，第一个被执行</span></span><br><span class="line">    <span class="comment">//将当前channel 加入到  channelGroup</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handlerAdded</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> ctx.channel();</span><br><span class="line">        <span class="comment">//将该客户加入聊天的信息推送给其它在线的客户端</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//该方法会将 channelGroup 中所有的channel 遍历，并发送消息，我们不需要自己遍历</span></span><br><span class="line"></span><br><span class="line">        channelGroup.writeAndFlush(<span class="string">&quot;[客户端]&quot;</span> + channel.remoteAddress() + <span class="string">&quot; 加入聊天&quot;</span> + sdf.format(<span class="keyword">new</span> <span class="title class_">java</span>.util.Date()) + <span class="string">&quot; \n&quot;</span>);</span><br><span class="line">        channelGroup.add(channel);</span><br><span class="line"></span><br><span class="line">		<span class="comment">//私聊如何实现</span></span><br><span class="line"><span class="comment">//         channels.put（&quot;userid100&quot;,channel）;</span></span><br><span class="line">		</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//断开连接, 将xx客户离开信息推送给当前在线的客户</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handlerRemoved</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> ctx.channel();</span><br><span class="line">        channelGroup.writeAndFlush(<span class="string">&quot;[客户端]&quot;</span> + channel.remoteAddress() + <span class="string">&quot; 离开了\n&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;channelGroup size&quot;</span> + channelGroup.size());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//表示channel 处于活动状态, 提示 xx上线</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//这个是给服务端看的，客户端上面已经提示xxx加入群聊了</span></span><br><span class="line">        System.out.println(ctx.channel().remoteAddress() + <span class="string">&quot; 上线了~&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//表示channel 处于不活动状态, 提示 xx离线了</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelInactive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(ctx.channel().remoteAddress() + <span class="string">&quot; 离线了~&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取数据，转发给在线的每一个客户端</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, String msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取到当前channel</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> ctx.channel();</span><br><span class="line">        <span class="comment">//这时我们遍历channelGroup, 根据不同的情况，回送不同的消息</span></span><br><span class="line"></span><br><span class="line">        channelGroup.forEach(ch -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span>(channel != ch) &#123; <span class="comment">//不是当前的channel,转发消息</span></span><br><span class="line">                ch.writeAndFlush(<span class="string">&quot;[客户]&quot;</span> + channel.remoteAddress() + <span class="string">&quot; 发送了消息&quot;</span> + msg + <span class="string">&quot;\n&quot;</span>);</span><br><span class="line">            &#125;<span class="keyword">else</span> &#123;<span class="comment">//回显自己发送的消息给自己</span></span><br><span class="line">                ch.writeAndFlush(<span class="string">&quot;[自己]发送了消息&quot;</span> + msg + <span class="string">&quot;\n&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//关闭通道</span></span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="GroupChatClient"><a href="#GroupChatClient" class="headerlink" title="GroupChatClient"></a>GroupChatClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.groupchat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GroupChatClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//属性</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String host;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> port;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GroupChatClient</span><span class="params">(String host, <span class="type">int</span> port)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.host = host;</span><br><span class="line">        <span class="built_in">this</span>.port = port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>()</span><br><span class="line">                .group(group)</span><br><span class="line">                .channel(NioSocketChannel.class)</span><br><span class="line">                .handler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">                        <span class="comment">//得到pipeline</span></span><br><span class="line">                        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                        <span class="comment">//加入相关handler</span></span><br><span class="line">                        pipeline.addLast(<span class="string">&quot;decoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">StringDecoder</span>());</span><br><span class="line">                        pipeline.addLast(<span class="string">&quot;encoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">StringEncoder</span>());</span><br><span class="line">                        <span class="comment">//加入自定义的handler</span></span><br><span class="line">                        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">GroupChatClientHandler</span>());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(host, port).sync();</span><br><span class="line">        <span class="comment">//得到channel</span></span><br><span class="line">            <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> channelFuture.channel();</span><br><span class="line">            System.out.println(<span class="string">&quot;-------&quot;</span> + channel.localAddress()+ <span class="string">&quot;--------&quot;</span>);</span><br><span class="line">            <span class="comment">//客户端需要输入信息，创建一个扫描器</span></span><br><span class="line">            <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">            <span class="keyword">while</span> (scanner.hasNextLine()) &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> scanner.nextLine();</span><br><span class="line">                <span class="comment">//通过channel 发送到服务器端</span></span><br><span class="line">                channel.writeAndFlush(msg + <span class="string">&quot;\r\n&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">GroupChatClient</span>(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">7000</span>).run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="GroupChatClientHandler"><a href="#GroupChatClientHandler" class="headerlink" title="GroupChatClientHandler"></a>GroupChatClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.groupchat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GroupChatClientHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;String&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//从服务器拿到的数据</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, String msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(msg.trim());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h2 id="Netty-心跳检测机制案例"><a href="#Netty-心跳检测机制案例" class="headerlink" title="Netty 心跳检测机制案例"></a>Netty 心跳检测机制案例</h2><p>实例要求：</p>
<ol>
<li>编写一个 <code>Netty</code> 心跳检测机制案例,当服务器超过 <code>3</code> 秒没有读时，就提示读空闲</li>
<li>当服务器超过 <code>5</code> 秒没有写操作时，就提示写空闲</li>
<li>实现当服务器超过 <code>7</code> 秒没有读或者写操作时，就提示读写空闲</li>
<li>代码如下：</li>
</ol>
<h3 id="MyServer"><a href="#MyServer" class="headerlink" title="MyServer"></a>MyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.heartbeat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.logging.LogLevel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.logging.LoggingHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.timeout.IdleStateHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建两个线程组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8个NioEventLoop</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            serverBootstrap.group(bossGroup, workerGroup);</span><br><span class="line">            serverBootstrap.channel(NioServerSocketChannel.class);</span><br><span class="line">            <span class="comment">//在bossGroup增加一个日志处理器</span></span><br><span class="line">            serverBootstrap.handler(<span class="keyword">new</span> <span class="title class_">LoggingHandler</span>(LogLevel.INFO));</span><br><span class="line">            serverBootstrap.childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                    <span class="comment">//加入一个netty 提供 IdleStateHandler</span></span><br><span class="line">                    <span class="comment">/*</span></span><br><span class="line"><span class="comment">                    说明</span></span><br><span class="line"><span class="comment">                    1. IdleStateHandler 是netty 提供的处理空闲状态的处理器</span></span><br><span class="line"><span class="comment">                    2. long readerIdleTime : 表示多长时间没有读, 就会发送一个心跳检测包检测是否连接</span></span><br><span class="line"><span class="comment">                    3. long writerIdleTime : 表示多长时间没有写, 就会发送一个心跳检测包检测是否连接</span></span><br><span class="line"><span class="comment">                    4. long allIdleTime : 表示多长时间没有读写, 就会发送一个心跳检测包检测是否连接</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">                    5. 文档说明</span></span><br><span class="line"><span class="comment">                    triggers an &#123;@link IdleStateEvent&#125; when a &#123;@link Channel&#125; has not performed</span></span><br><span class="line"><span class="comment">                   read, write, or both operation for a while.</span></span><br><span class="line"><span class="comment">                    6. 当 IdleStateEvent 触发后 , 就会传递给管道 的下一个handler去处理，通过调用(触发)</span></span><br><span class="line"><span class="comment">                   下一个handler 的 userEventTiggered , 在该方法中去处理 IdleStateEvent(读空闲，写空闲，读写空闲)</span></span><br><span class="line"><span class="comment">                    7.handlerRemoved有时候是无法感知连接断掉，所以还是需要心跳包的检测来判断连接是否还有效</span></span><br><span class="line"><span class="comment">                     */</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">IdleStateHandler</span>(<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>, TimeUnit.SECONDS));</span><br><span class="line">                    <span class="comment">//加入一个对空闲检测进一步处理的handler(自定义)</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyServerHandler</span>());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//启动服务器</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">7000</span>).sync();</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="MyServerHandler"><a href="#MyServerHandler" class="headerlink" title="MyServerHandler"></a>MyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.heartbeat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.timeout.IdleStateEvent;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ctx 上下文</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> evt 事件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">userEventTriggered</span><span class="params">(ChannelHandlerContext ctx, Object evt)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(evt <span class="keyword">instanceof</span> IdleStateEvent) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//将  evt 向下转型 IdleStateEvent</span></span><br><span class="line">            <span class="type">IdleStateEvent</span> <span class="variable">event</span> <span class="operator">=</span> (IdleStateEvent) evt;</span><br><span class="line">            <span class="type">String</span> <span class="variable">eventType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">switch</span> (event.state()) &#123;</span><br><span class="line">                <span class="keyword">case</span> READER_IDLE:</span><br><span class="line">                  eventType = <span class="string">&quot;读空闲&quot;</span>;</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> WRITER_IDLE:</span><br><span class="line">                    eventType = <span class="string">&quot;写空闲&quot;</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> ALL_IDLE:</span><br><span class="line">                    eventType = <span class="string">&quot;读写空闲&quot;</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(ctx.channel().remoteAddress() + <span class="string">&quot;--超时时间--&quot;</span> + eventType);</span><br><span class="line">            System.out.println(<span class="string">&quot;服务器做相应处理..&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//如果发生空闲，我们关闭通道</span></span><br><span class="line">           <span class="comment">// ctx.channel().close();</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="Netty-通过-WebSocket-编程实现服务器和客户端长连接"><a href="#Netty-通过-WebSocket-编程实现服务器和客户端长连接" class="headerlink" title="Netty 通过 WebSocket 编程实现服务器和客户端长连接"></a>Netty 通过 WebSocket 编程实现服务器和客户端长连接</h2><p>实例要求：</p>
<ol>
<li><code>Http</code> 协议是无状态的，浏览器和服务器间的请求响应一次，下一次会重新创建连接。</li>
<li>要求：实现基于 <code>WebSocket</code> 的长连接的全双工的交互</li>
<li>改变 <code>Http</code> 协议多次请求的约束，实现长连接了，服务器可以发送消息给浏览器</li>
<li>客户端浏览器和服务器端会相互感知，比如服务器关闭了，浏览器会感知，同样浏览器关闭了，服务器会感知</li>
<li>运行界面</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>



<h3 id="MyServer-1"><a href="#MyServer-1" class="headerlink" title="MyServer"></a>MyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.websocket;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.heartbeat.MyServerHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.HttpObjectAggregator;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.HttpServerCodec;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.logging.LogLevel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.logging.LoggingHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.stream.ChunkedWriteHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.timeout.IdleStateHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建两个线程组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8个NioEventLoop</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            serverBootstrap.group(bossGroup, workerGroup);</span><br><span class="line">            serverBootstrap.channel(NioServerSocketChannel.class);</span><br><span class="line">            serverBootstrap.handler(<span class="keyword">new</span> <span class="title class_">LoggingHandler</span>(LogLevel.INFO));</span><br><span class="line">            serverBootstrap.childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//因为基于http协议，使用http的编码和解码器</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">HttpServerCodec</span>());</span><br><span class="line">                    <span class="comment">//http是以块方式写，添加ChunkedWriteHandler处理器</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">ChunkedWriteHandler</span>());</span><br><span class="line"></span><br><span class="line">                    <span class="comment">/*</span></span><br><span class="line"><span class="comment">                    说明</span></span><br><span class="line"><span class="comment">                    1. http数据在传输过程中是分段, HttpObjectAggregator ，就是可以将多个段聚合</span></span><br><span class="line"><span class="comment">                    2. 这就就是为什么，当浏览器发送大量数据时，就会发出多次http请求</span></span><br><span class="line"><span class="comment">                     */</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">HttpObjectAggregator</span>(<span class="number">8192</span>));</span><br><span class="line">                    <span class="comment">/*</span></span><br><span class="line"><span class="comment">                    说明</span></span><br><span class="line"><span class="comment">                    1. 对应websocket ，它的数据是以 帧(frame) 形式传递</span></span><br><span class="line"><span class="comment">                    2. 可以看到WebSocketFrame 下面有六个子类</span></span><br><span class="line"><span class="comment">                    3. 浏览器请求时 ws://localhost:7000/hello 表示请求的uri</span></span><br><span class="line"><span class="comment">                    4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接</span></span><br><span class="line"><span class="comment">                    5. 是通过一个 状态码 101</span></span><br><span class="line"><span class="comment">                     */</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">WebSocketServerProtocolHandler</span>(<span class="string">&quot;/hello&quot;</span>));</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//自定义的handler ，处理业务逻辑</span></span><br><span class="line">                    pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyTextWebSocketFrameHandler</span>());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//启动服务器</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">7000</span>).sync();</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="MyTextWebSocketFrameHandler"><a href="#MyTextWebSocketFrameHandler" class="headerlink" title="MyTextWebSocketFrameHandler"></a>MyTextWebSocketFrameHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.websocket;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.http.websocketx.TextWebSocketFrame;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.time.LocalDateTime;</span><br><span class="line"></span><br><span class="line"><span class="comment">//这里 TextWebSocketFrame 类型，表示一个文本帧(frame)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyTextWebSocketFrameHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;TextWebSocketFrame&gt;&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, TextWebSocketFrame msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器收到消息 &quot;</span> + msg.text());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//回复消息</span></span><br><span class="line">        ctx.channel().writeAndFlush(<span class="keyword">new</span> <span class="title class_">TextWebSocketFrame</span>(<span class="string">&quot;服务器时间&quot;</span> + LocalDateTime.now() + <span class="string">&quot; &quot;</span> + msg.text()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当web客户端连接后， 触发方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handlerAdded</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//id 表示唯一的值，LongText 是唯一的 ShortText 不是唯一</span></span><br><span class="line">        System.out.println(<span class="string">&quot;handlerAdded 被调用&quot;</span> + ctx.channel().id().asLongText());</span><br><span class="line">        System.out.println(<span class="string">&quot;handlerAdded 被调用&quot;</span> + ctx.channel().id().asShortText());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handlerRemoved</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;handlerRemoved 被调用&quot;</span> + ctx.channel().id().asLongText());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;异常发生 &quot;</span> + cause.getMessage());</span><br><span class="line">        ctx.close(); <span class="comment">//关闭连接</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="hello-html"><a href="#hello-html" class="headerlink" title="hello.html"></a>hello.html</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> socket;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//判断当前浏览器是否支持websocket</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span>(<span class="variable language_">window</span>.<span class="property">WebSocket</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//go on</span></span></span><br><span class="line"><span class="language-javascript">        socket = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">&quot;ws://localhost:7000/hello2&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//相当于channelReado, ev 收到服务器端回送的消息</span></span></span><br><span class="line"><span class="language-javascript">        socket.<span class="property">onmessage</span> = <span class="keyword">function</span> (<span class="params">ev</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> rt = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;responseText&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">            rt.<span class="property">value</span> = rt.<span class="property">value</span> + <span class="string">&quot;\n&quot;</span> + ev.<span class="property">data</span>;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//相当于连接开启(感知到连接开启)</span></span></span><br><span class="line"><span class="language-javascript">        socket.<span class="property">onopen</span> = <span class="keyword">function</span> (<span class="params">ev</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> rt = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;responseText&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">            rt.<span class="property">value</span> = <span class="string">&quot;连接开启了..&quot;</span></span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//相当于连接关闭(感知到连接关闭)</span></span></span><br><span class="line"><span class="language-javascript">        socket.<span class="property">onclose</span> = <span class="keyword">function</span> (<span class="params">ev</span>) &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> rt = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;responseText&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">            rt.<span class="property">value</span> = rt.<span class="property">value</span> + <span class="string">&quot;\n&quot;</span> + <span class="string">&quot;连接关闭了..&quot;</span></span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">alert</span>(<span class="string">&quot;当前浏览器不支持websocket&quot;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//发送消息到服务器</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">send</span>(<span class="params">message</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">if</span>(!<span class="variable language_">window</span>.<span class="property">socket</span>) &#123; <span class="comment">//先判断socket是否创建好</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">if</span>(socket.<span class="property">readyState</span> == <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">//通过socket 发送消息</span></span></span><br><span class="line"><span class="language-javascript">            socket.<span class="title function_">send</span>(message)</span></span><br><span class="line"><span class="language-javascript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">alert</span>(<span class="string">&quot;连接没有开启&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">onsubmit</span>=<span class="string">&quot;return false&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">textarea</span> <span class="attr">name</span>=<span class="string">&quot;message&quot;</span> <span class="attr">style</span>=<span class="string">&quot;height: 300px; width: 300px&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;button&quot;</span> <span class="attr">value</span>=<span class="string">&quot;发生消息&quot;</span> <span class="attr">onclick</span>=<span class="string">&quot;send(this.form.message.value)&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">&quot;responseText&quot;</span> <span class="attr">style</span>=<span class="string">&quot;height: 300px; width: 300px&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;button&quot;</span> <span class="attr">value</span>=<span class="string">&quot;清空内容&quot;</span> <span class="attr">onclick</span>=<span class="string">&quot;document.getElementById(&#x27;responseText&#x27;).value=&#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>



<p>可以看到并不是发一次数据，连接就关闭了，而是可以继续发送。</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>









</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="http://example.com">Alix</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://example.com/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/">http://example.com/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://example.com" target="_blank">石银的小酒屋</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Netty/">Netty</a></div><div class="post_share"><div class="social-share" data-image="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/"><img class="prev-cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Netty入门-第三话</div></div></a></div><div class="next-post pull-right"><a href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/"><img class="next-cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Netty入门-第一话</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话"><img class="cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-08</div><div class="title">Netty入门-第一话</div></div></a></div><div><a href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/" title="Netty入门-第三话"><img class="cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-21</div><div class="title">Netty入门-第三话</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div><div id="comment-switch"><span class="first-comment">Valine</span><span class="switch-btn"></span><span class="second-comment">Disqus</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div><div><div id="disqus_thread"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/assetse/%E5%A4%B4%E5%83%8F.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">Alix</div><div class="author-info__description">归途也还可爱</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">23</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/923584731"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/923584731" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:923584731@qq.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a><a class="social-icon" href="/atom.xml" target="_blank" title="Rss"><i class="fas fa-rss"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">少年郎的博客网站</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Netty-%E6%A6%82%E8%BF%B0"><span class="toc-number">1.</span> <span class="toc-text">Netty 概述</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8E%9F%E7%94%9F-NIO-%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-number">1.1.</span> <span class="toc-text">原生 NIO 存在的问题</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E5%AE%98%E7%BD%91%E8%AF%B4%E6%98%8E"><span class="toc-number">1.2.</span> <span class="toc-text">Netty 官网说明</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E7%9A%84%E4%BC%98%E7%82%B9"><span class="toc-number">1.3.</span> <span class="toc-text">Netty 的优点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E"><span class="toc-number">1.4.</span> <span class="toc-text">Netty 版本说明</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Netty-%E9%AB%98%E6%80%A7%E8%83%BD%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1"><span class="toc-number">2.</span> <span class="toc-text">Netty 高性能架构设计</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D"><span class="toc-number">2.1.</span> <span class="toc-text">线程模型基本介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BC%A0%E7%BB%9F%E9%98%BB%E5%A1%9E-I-x2F-O-%E6%9C%8D%E5%8A%A1%E6%A8%A1%E5%9E%8B"><span class="toc-number">2.2.</span> <span class="toc-text">传统阻塞 I&#x2F;O 服务模型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%9B%BE"><span class="toc-number">2.2.1.</span> <span class="toc-text">工作原理图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A8%A1%E5%9E%8B%E7%89%B9%E7%82%B9"><span class="toc-number">2.2.2.</span> <span class="toc-text">模型特点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90"><span class="toc-number">2.2.3.</span> <span class="toc-text">问题分析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Reactor-%E6%A8%A1%E5%BC%8F"><span class="toc-number">2.3.</span> <span class="toc-text">Reactor 模式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%92%88%E5%AF%B9%E4%BC%A0%E7%BB%9F%E9%98%BB%E5%A1%9E-I-x2F-O-%E6%9C%8D%E5%8A%A1%E6%A8%A1%E5%9E%8B%E7%9A%84-2-%E4%B8%AA%E7%BC%BA%E7%82%B9%EF%BC%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%EF%BC%9A"><span class="toc-number">2.3.1.</span> <span class="toc-text">针对传统阻塞 I&#x2F;O 服务模型的 2 个缺点，解决方案：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#I-x2F-O-%E5%A4%8D%E7%94%A8%E7%BB%93%E5%90%88%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%8C%E5%B0%B1%E6%98%AF-Reactor-%E6%A8%A1%E5%BC%8F%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E6%80%9D%E6%83%B3%EF%BC%8C%E5%A6%82%E5%9B%BE"><span class="toc-number">2.3.2.</span> <span class="toc-text">I&#x2F;O 复用结合线程池，就是 Reactor 模式基本设计思想，如图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reactor-%E6%A8%A1%E5%BC%8F%E4%B8%AD%E6%A0%B8%E5%BF%83%E7%BB%84%E6%88%90"><span class="toc-number">2.3.3.</span> <span class="toc-text">Reactor 模式中核心组成</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reactor-%E6%A8%A1%E5%BC%8F%E5%88%86%E7%B1%BB"><span class="toc-number">2.3.4.</span> <span class="toc-text">Reactor 模式分类</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%95-Reactor-%E5%8D%95%E7%BA%BF%E7%A8%8B"><span class="toc-number">2.4.</span> <span class="toc-text">单 Reactor 单线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E8%AF%B4%E6%98%8E"><span class="toc-number">2.4.1.</span> <span class="toc-text">方案说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E4%BC%98%E7%BC%BA%E7%82%B9%E5%88%86%E6%9E%90"><span class="toc-number">2.4.2.</span> <span class="toc-text">方案优缺点分析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%95-Reactor-%E5%A4%9A%E7%BA%BF%E7%A8%8B"><span class="toc-number">2.5.</span> <span class="toc-text">单 Reactor 多线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E8%AF%B4%E6%98%8E-1"><span class="toc-number">2.5.1.</span> <span class="toc-text">方案说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E4%BC%98%E7%BC%BA%E7%82%B9%E5%88%86%E6%9E%90-1"><span class="toc-number">2.5.2.</span> <span class="toc-text">方案优缺点分析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%BB%E4%BB%8E-Reactor-%E5%A4%9A%E7%BA%BF%E7%A8%8B"><span class="toc-number">2.6.</span> <span class="toc-text">主从 Reactor 多线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%9B%BE-1"><span class="toc-number">2.6.1.</span> <span class="toc-text">工作原理图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Scalable-IO-in-Java-%E5%AF%B9-Multiple-Reactors-%E7%9A%84%E5%8E%9F%E7%90%86%E5%9B%BE%E8%A7%A3"><span class="toc-number">2.6.2.</span> <span class="toc-text">Scalable IO in Java 对 Multiple Reactors 的原理图解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E4%BC%98%E7%BC%BA%E7%82%B9%E8%AF%B4%E6%98%8E"><span class="toc-number">2.6.3.</span> <span class="toc-text">方案优缺点说明</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Reactor-%E6%A8%A1%E5%BC%8F%E5%B0%8F%E7%BB%93"><span class="toc-number">2.7.</span> <span class="toc-text">Reactor 模式小结</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E7%A7%8D%E6%A8%A1%E5%BC%8F%E7%94%A8%E7%94%9F%E6%B4%BB%E6%A1%88%E4%BE%8B%E6%9D%A5%E7%90%86%E8%A7%A3"><span class="toc-number">2.7.1.</span> <span class="toc-text">3 种模式用生活案例来理解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reactor-%E6%A8%A1%E5%BC%8F%E5%85%B7%E6%9C%89%E5%A6%82%E4%B8%8B%E7%9A%84%E4%BC%98%E7%82%B9"><span class="toc-number">2.7.2.</span> <span class="toc-text">Reactor 模式具有如下的优点</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E6%A8%A1%E5%9E%8B"><span class="toc-number">2.8.</span> <span class="toc-text">Netty 模型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E7%A4%BA%E6%84%8F%E5%9B%BE1-%E7%AE%80%E5%8D%95%E7%89%88"><span class="toc-number">2.8.1.</span> <span class="toc-text">工作原理示意图1 - 简单版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E7%A4%BA%E6%84%8F%E5%9B%BE2-%E8%BF%9B%E9%98%B6%E7%89%88"><span class="toc-number">2.8.2.</span> <span class="toc-text">工作原理示意图2 - 进阶版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E7%A4%BA%E6%84%8F%E5%9B%BE3-%E8%AF%A6%E7%BB%86%E7%89%88"><span class="toc-number">2.8.3.</span> <span class="toc-text">工作原理示意图3 - 详细版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Netty-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%AE%9E%E4%BE%8B-TCP-%E6%9C%8D%E5%8A%A1"><span class="toc-number">2.8.4.</span> <span class="toc-text">Netty 快速入门实例 - TCP 服务</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#NettyServer"><span class="toc-number">2.8.4.1.</span> <span class="toc-text">NettyServer</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#NettyServerHandler"><span class="toc-number">2.8.4.2.</span> <span class="toc-text">NettyServerHandler</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#NettyClient"><span class="toc-number">2.8.4.3.</span> <span class="toc-text">NettyClient</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#NettyClientHandler"><span class="toc-number">2.8.4.4.</span> <span class="toc-text">NettyClientHandler</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97%E4%B8%AD%E7%9A%84-Task-%E6%9C%89-3-%E7%A7%8D%E5%85%B8%E5%9E%8B%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-number">2.8.5.</span> <span class="toc-text">任务队列中的 Task 有 3 种典型使用场景</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E5%86%8D%E8%AF%B4%E6%98%8E"><span class="toc-number">2.8.6.</span> <span class="toc-text">方案再说明</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E6%A8%A1%E5%9E%8B"><span class="toc-number">2.9.</span> <span class="toc-text">异步模型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D"><span class="toc-number">2.9.1.</span> <span class="toc-text">基本介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Future-%E8%AF%B4%E6%98%8E"><span class="toc-number">2.9.2.</span> <span class="toc-text">Future 说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E7%A4%BA%E6%84%8F%E5%9B%BE"><span class="toc-number">2.9.3.</span> <span class="toc-text">工作原理示意图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Future-Listener-%E6%9C%BA%E5%88%B6"><span class="toc-number">2.9.4.</span> <span class="toc-text">Future-Listener 机制</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%AE%9E%E4%BE%8B-HTTP%E6%9C%8D%E5%8A%A1"><span class="toc-number">2.10.</span> <span class="toc-text">快速入门实例 - HTTP服务</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#TestServer"><span class="toc-number">2.10.1.</span> <span class="toc-text">TestServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#TestServerInitializer"><span class="toc-number">2.10.2.</span> <span class="toc-text">TestServerInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#TestHttpServerHandler"><span class="toc-number">2.10.3.</span> <span class="toc-text">TestHttpServerHandler</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Netty-%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97%E7%BB%84%E4%BB%B6"><span class="toc-number">3.</span> <span class="toc-text">Netty 核心模块组件</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Bootstrap%E3%80%81ServerBootstrap"><span class="toc-number">3.1.</span> <span class="toc-text">Bootstrap、ServerBootstrap</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Future%E3%80%81ChannelFuture"><span class="toc-number">3.2.</span> <span class="toc-text">Future、ChannelFuture</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Channel"><span class="toc-number">3.3.</span> <span class="toc-text">Channel</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Selector"><span class="toc-number">3.4.</span> <span class="toc-text">Selector</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ChannelHandler-%E5%8F%8A%E5%85%B6%E5%AE%9E%E7%8E%B0%E7%B1%BB"><span class="toc-number">3.5.</span> <span class="toc-text">ChannelHandler 及其实现类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Pipeline-%E5%92%8C-ChannelPipeline"><span class="toc-number">3.6.</span> <span class="toc-text">Pipeline 和 ChannelPipeline</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ChannelHandlerContext"><span class="toc-number">3.7.</span> <span class="toc-text">ChannelHandlerContext</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ChannelOption"><span class="toc-number">3.8.</span> <span class="toc-text">ChannelOption</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#EventLoopGroup-%E5%92%8C%E5%85%B6%E5%AE%9E%E7%8E%B0%E7%B1%BB-NioEventLoopGroup"><span class="toc-number">3.9.</span> <span class="toc-text">EventLoopGroup 和其实现类 NioEventLoopGroup</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Unpooled-%E7%B1%BB"><span class="toc-number">3.10.</span> <span class="toc-text">Unpooled 类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E5%BA%94%E7%94%A8%E5%AE%9E%E4%BE%8B-%E7%BE%A4%E8%81%8A%E7%B3%BB%E7%BB%9F"><span class="toc-number">3.11.</span> <span class="toc-text">Netty 应用实例-群聊系统</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#GroupChatServer"><span class="toc-number">3.11.1.</span> <span class="toc-text">GroupChatServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#GroupChatServerHandler"><span class="toc-number">3.11.2.</span> <span class="toc-text">GroupChatServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#GroupChatClient"><span class="toc-number">3.11.3.</span> <span class="toc-text">GroupChatClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#GroupChatClientHandler"><span class="toc-number">3.11.4.</span> <span class="toc-text">GroupChatClientHandler</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E5%BF%83%E8%B7%B3%E6%A3%80%E6%B5%8B%E6%9C%BA%E5%88%B6%E6%A1%88%E4%BE%8B"><span class="toc-number">3.12.</span> <span class="toc-text">Netty 心跳检测机制案例</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServer"><span class="toc-number">3.12.1.</span> <span class="toc-text">MyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerHandler"><span class="toc-number">3.12.2.</span> <span class="toc-text">MyServerHandler</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E9%80%9A%E8%BF%87-WebSocket-%E7%BC%96%E7%A8%8B%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%95%BF%E8%BF%9E%E6%8E%A5"><span class="toc-number">3.13.</span> <span class="toc-text">Netty 通过 WebSocket 编程实现服务器和客户端长连接</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServer-1"><span class="toc-number">3.13.1.</span> <span class="toc-text">MyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyTextWebSocketFrameHandler"><span class="toc-number">3.13.2.</span> <span class="toc-text">MyTextWebSocketFrameHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#hello-html"><span class="toc-number">3.13.3.</span> <span class="toc-text">hello.html</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2023/04/20/%E9%94%81%E6%9C%BA%E5%88%B6/" title="数据库并发锁机制"><img src="/assetse/y13.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="数据库并发锁机制"/></a><div class="content"><a class="title" href="/2023/04/20/%E9%94%81%E6%9C%BA%E5%88%B6/" title="数据库并发锁机制">数据库并发锁机制</a><time datetime="2023-04-20T06:55:04.000Z" title="发表于 2023-04-20 14:55:04">2023-04-20</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/" title="Netty入门-第三话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第三话"/></a><div class="content"><a class="title" href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/" title="Netty入门-第三话">Netty入门-第三话</a><time datetime="2022-04-21T09:38:58.000Z" title="发表于 2022-04-21 17:38:58">2022-04-21</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/" title="Netty入门-第二话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第二话"/></a><div class="content"><a class="title" href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/" title="Netty入门-第二话">Netty入门-第二话</a><time datetime="2022-04-15T06:21:58.000Z" title="发表于 2022-04-15 14:21:58">2022-04-15</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第一话"/></a><div class="content"><a class="title" href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话">Netty入门-第一话</a><time datetime="2022-04-08T06:21:58.000Z" title="发表于 2022-04-08 14:21:58">2022-04-08</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/11/19/JVM/JVM%E7%B3%BB%E5%88%97-%E7%AC%AC12%E7%AB%A0-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/" title="JVM系列-第12章-垃圾回收器"><img src="https://npm.elemecdn.com/lql_static@latest/logo/jvm.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="JVM系列-第12章-垃圾回收器"/></a><div class="content"><a class="title" href="/2021/11/19/JVM/JVM%E7%B3%BB%E5%88%97-%E7%AC%AC12%E7%AB%A0-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/" title="JVM系列-第12章-垃圾回收器">JVM系列-第12章-垃圾回收器</a><time datetime="2021-11-19T10:33:24.000Z" title="发表于 2021-11-19 18:33:24">2021-11-19</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By Alix</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script>var preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',preloader.endLoading())</script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: '',
      appKey: '',
      avatar: 'monsterid',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: false
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !true) {
  if (true) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script><script>function loadDisqus () {
  var disqus_config = function () {
    this.page.url = 'http://example.com/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/'
    this.page.identifier = '/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/'
    this.page.title = 'Netty入门-第二话'
  };

  window.disqusReset = () => {
    DISQUS.reset({
      reload: true,
      config: disqus_config
    })
  }

  if (window.DISQUS) disqusReset()
  else {
    (function() { 
      var d = document, s = d.createElement('script');
      s.src = 'https://.disqus.com/embed.js';
      s.setAttribute('data-timestamp', +new Date());
      (d.head || d.body).appendChild(s);
    })();
  }

  document.getElementById('darkmode').addEventListener('click', () => {
    setTimeout(() => window.disqusReset(), 200)
  })
}

if ('Valine' === 'Disqus' || !true) {
  if (true) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)
  else loadDisqus()
} else {
  function loadOtherComment () {
    loadDisqus()
  }
}
</script></div><script async src="https://npm.elemecdn.com/tzy-blog/lib/js/other/sakura.js"></script><script defer="defer" id="ribbon" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="false"></script><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-fluttering-ribbon.min.js"></script><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-show-text.min.js" data-mobile="false" data-text="你好,大帅哥,大美女" data-fontsize="15px" data-random="true" async="async"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>let pjaxSelectors = ["head > title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"]

var pjax = new Pjax({
  elements: 'a:not([target="_blank"])',
  selectors: pjaxSelectors,
  cacheBust: false,
  analytics: false,
  scrollRestoration: false
})

document.addEventListener('pjax:send', function () {

  // removeEventListener scroll 
  window.tocScrollFn && window.removeEventListener('scroll', window.tocScrollFn)
  window.scrollCollect && window.removeEventListener('scroll', scrollCollect)

  typeof preloader === 'object' && preloader.initLoading()
  document.getElementById('rightside').style.cssText = "opacity: ''; transform: ''"
  
  if (window.aplayers) {
    for (let i = 0; i < window.aplayers.length; i++) {
      if (!window.aplayers[i].options.fixed) {
        window.aplayers[i].destroy()
      }
    }
  }

  typeof typed === 'object' && typed.destroy()

  //reset readmode
  const $bodyClassList = document.body.classList
  $bodyClassList.contains('read-mode') && $bodyClassList.remove('read-mode')

  typeof disqusjs === 'object' && disqusjs.destroy()
})

document.addEventListener('pjax:complete', function () {
  window.refreshFn()

  document.querySelectorAll('script[data-pjax]').forEach(item => {
    const newScript = document.createElement('script')
    const content = item.text || item.textContent || item.innerHTML || ""
    Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
    newScript.appendChild(document.createTextNode(content))
    item.parentNode.replaceChild(newScript, item)
  })

  GLOBAL_CONFIG.islazyload && window.lazyLoadInstance.update()

  typeof chatBtnFn === 'function' && chatBtnFn()
  typeof panguInit === 'function' && panguInit()

  // google analytics
  typeof gtag === 'function' && gtag('config', '', {'page_path': window.location.pathname});

  // baidu analytics
  typeof _hmt === 'object' && _hmt.push(['_trackPageview',window.location.pathname]);

  typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()

  // prismjs
  typeof Prism === 'object' && Prism.highlightAll()

  typeof preloader === 'object' && preloader.endLoading()
})

document.addEventListener('pjax:error', (e) => {
  if (e.request.status === 404) {
    pjax.loadUrl('/404.html')
  }
})</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"tagMode":false,"debug":false,"model":{"jsonPath":"/live2dw/assets/shizuku.model.json"},"display":{"position":"right","width":150,"height":300,"hOffset":20,"vOffset":-20},"mobile":{"show":false},"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/"});</script></body></html>